package com.cgwx.tcservice.controller;

import com.alibaba.excel.EasyExcel;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.cgwx.commonutils.result.Result;
import com.cgwx.springsecurity.annotation.Log;
import com.cgwx.springsecurity.enums.BusinessType;
import com.cgwx.tcservice.config.CanConfig;
import com.cgwx.tcservice.config.CanPortConfig;
import com.cgwx.tcservice.config.SPortConfig;
import com.cgwx.tcservice.entity.bean.TcCommandConfigMap;
import com.cgwx.tcservice.entity.bean.TcFrameConfigIdToIdentified;
import com.cgwx.tcservice.entity.bean.TcSendParam;
import com.cgwx.tcservice.entity.tc.TcCommandConfig;
import com.cgwx.tcservice.entity.tc.TcFrameConfig;
import com.cgwx.tcservice.entity.tc.TcInfoLog;
import com.cgwx.tcservice.entity.tc.TcParameterConfig;
import com.cgwx.tcservice.entity.vo.TcCommandDisplayVo;
import com.cgwx.tcservice.entity.vo.TcFrameDisplayVo;
import com.cgwx.tcservice.entity.vo.TcInfoLogExcelVo;
import com.cgwx.tcservice.netty.NettyUdpSendServer;
import com.cgwx.tcservice.netty.UdpSendInitApplication;
import com.cgwx.tcservice.service.*;
import com.cgwx.tcservice.utils.SendDataAnalysis;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiParam;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * <p>
 * 遥控前端控制器
 * </p>
 *
 * @author GaoWei
 * @since 2023-09-14
 */

@Api(tags = "遥控前端控制器")
@RestController
@RequestMapping("/tcservice/tc")
@CrossOrigin
public class TcController {

    @Autowired
    private TcFrameConfigService tcFrameConfigService;
    @Autowired
    private TcCommandConfigService tcCommandConfigService;
    @Autowired
    private TcParameterConfigService tcParameterConfigService;
    @Autowired
    private TcDirectFrameConfigService tcDirectFrameConfigService;
    @Autowired
    private TcInfoLogService tcInfoLogService;
    @Autowired
    private UdpSendInitApplication udpSendInitApplication;
    @Autowired
    private SendDataAnalysis sendDataAnalysis;
    @Autowired
    private NettyUdpSendServer nettyUdpSendServer;
    @Autowired
    private CanPortConfig canPortConfig;
    @Autowired
    private SPortConfig sPortConfig;
    @Autowired
    private CanConfig canConfig;

    @Log(title = "上传遥控注入指令配置", businessType = BusinessType.TC_CONFIG_UPLOAD)
    @ApiOperation(value = "上传注入指令配置表格")
    @PostMapping("updateInjectionTcConfig")
    public Result updateInjectionTcConfig(
            @ApiParam(name = "file", value = "待上传的注入指令配置表格") MultipartFile file){
//        判断上传文件是否为空
        if(file.getOriginalFilename() != null)
        {
//            遥控帧插入或更新操作
            tcFrameConfigService.updateTcFrameConfig(file);
//            遥控指令插入或更新操作，返回遥测指令代号集合
            Map<String, String> sheetToTcCommandCode = tcCommandConfigService.updateTcCommandConfig(file, tcCommandConfigService);
//            遥控参数插入或更新操作
            tcParameterConfigService.updateTcParameterConfig(file,tcParameterConfigService,sheetToTcCommandCode);

//            更新遥控配置全局变量
            udpSendInitApplication.tcConfigInit();

            return Result.ok().data("message", "注入指令配置表格上传成功");
        }else {
            return Result.error().data("message", "注入指令配置表格上传失败");
        }
    }

    @Log(title = "上传遥控直接指令配置", businessType = BusinessType.TC_CONFIG_UPLOAD)
    @ApiOperation(value = "上传直接指令配置表格")
    @PostMapping("updateDirectTcConfig")
    public Result updateDirectTcConfig(
            @ApiParam(name = "file", value = "待上传的直接指令配置表格") MultipartFile file){
//        判断上传文件是否为空
        if(file.getOriginalFilename() != null)
        {
//            直接指令遥控帧插入或更新操作
            tcDirectFrameConfigService.updateTcDirectFrameConfig(file,tcDirectFrameConfigService);

//            更新遥控配置全局变量 todo
//            udpInitApplication.tmConfigInit();

            return Result.ok().data("message", "直接指令配置表格上传成功");
        }else {
            return Result.error().data("message", "直接指令配置表格上传失败");
        }
    }

    @ApiOperation(value = "查询遥控配置")
    @GetMapping("getAllTcConfig")
    public Result getAllTcConfig(){
//        创建用于封装最终展示数据的实体类
        List<TcFrameDisplayVo> tcList = new ArrayList<>();
//        遍历所有遥控帧，并在遍历时封装进此帧下的所有遥控指令
//        首先查询所有遥控帧
        List<TcFrameConfig> tcFrameConfigList = tcFrameConfigService.list(null);
//        遍历遥控帧，封装进最终展示数据的实体类
        for (TcFrameConfig tcFrameConfig : tcFrameConfigList) {
//            准备封装的一级节点，遥控帧名称及其下所有遥控指令集合
            TcFrameDisplayVo tcFrameDisplayVo = new TcFrameDisplayVo();
//            封装遥控帧名称
            tcFrameDisplayVo.setDisplayTitle(tcFrameConfig.getTcFrameName());
            tcFrameDisplayVo.setId(tcFrameConfig.getId());
//            创建二级节点，用于封装当前遥控帧下的所有遥控指令
            List<TcCommandDisplayVo> tcCommandDisplayVoList = new ArrayList<>();
//            得到当前遍历的遥控帧标识，作为条件遍历遥控指令表，查询出所有属于此帧的遥控指令
            String tcFrameIdentifiers = tcFrameConfig.getTcFrameIdentifiers();
            QueryWrapper<TcCommandConfig> queryWrapper = new QueryWrapper<>();
            queryWrapper.eq("tc_frame_identifiers",tcFrameIdentifiers);
            List<TcCommandConfig> tcCommandConfigList = tcCommandConfigService.list(queryWrapper);
//            封装全部二级节点
            for (TcCommandConfig tcCommandConfig : tcCommandConfigList) {
                TcCommandDisplayVo tcCommandDisplayVo = new TcCommandDisplayVo();
                String displayTitle = '[' + tcCommandConfig.getTcCommandCode() + ']' + tcCommandConfig.getTcCommandName();
                tcCommandDisplayVo.setDisplayTitle(displayTitle);
                tcCommandDisplayVo.setId(tcCommandConfig.getId());
                tcCommandDisplayVoList.add(tcCommandDisplayVo);
            }
//            将二级节点封装进一级节点
            tcFrameDisplayVo.setDisplayChildren(tcCommandDisplayVoList);
//            将封装好的一级节点封装进最终展示数据
            tcList.add(tcFrameDisplayVo);
        }
        return Result.ok().data("tcList",tcList);
    }

    @ApiOperation(value = "根据ID查询遥控指令参数配置")
    @GetMapping("getTcParamConfigById/{id}")
    public Result getTcParamConfigById(
            @ApiParam(name = "id", value = "待查询的遥控指令配置id")
            @PathVariable String id){
        if(TcFrameConfigIdToIdentified.TC_FRAME_ID_IDENTIFIED.get(id) != null){
//            说明前端选中的是遥控帧的id
            return Result.ok();
        }else{
//            说明前端选中的是遥控指令的id，据此得到遥控代号
            String tcCommandCode = TcCommandConfigMap.TC_COMMAND_ID_TCCOMMANDCONFIG.get(id).getTcCommandCode();
//            根据遥控代号得到此遥控指令下的所有遥控参数配置
//            ArrayList<TcParameterConfig> tcParameterConfigList = TcParameterConfigMap.TC_TCCODE_TO_PARAMETER_CONFIG_LIST.get(tcCommandCode);
//            不能查静态变量，因为要上一次用户填写的值，需要查库
            QueryWrapper<TcParameterConfig> queryWrapper = new QueryWrapper<>();
            queryWrapper.eq("tc_command_code",tcCommandCode);
            List<TcParameterConfig> tcParameterConfigList = tcParameterConfigService.list(queryWrapper);
            return Result.ok().data("tcParameterConfigList", tcParameterConfigList);
        }
    }

    @ApiOperation(value = "根据ID判断是遥控帧还是遥控指令")
    @GetMapping("queryIsFrameOrCommandById/{id}")
    public Result queryIsFrameOrCommandById(
            @ApiParam(name = "id", value = "待查询id")
            @PathVariable String id){
        if(TcFrameConfigIdToIdentified.TC_FRAME_ID_IDENTIFIED.get(id) != null){
//            说明前端选中的是遥控帧的id
            return Result.ok().data("id", "frame");
        }else{
//            说明前端选中的是遥控指令的id
            return Result.ok().data("id", "command");
        }
    }

    @Log(title = "删除遥控指令配置", businessType = BusinessType.TC_CONFIG_DELETE)
    @ApiOperation(value = "根据遥控帧配置ID，删除遥控帧配置和对应的遥控指令配置、遥控参数配置")
    @GetMapping("deleteTcFrameById/{id}")
    public Result deleteTcFrameById(
            @ApiParam(name = "id", value = "待删除的遥控帧配置id")
            @PathVariable String id){
//        是否为遥控帧id的判断在前端实现，此处传递过来的确定为遥控帧配置ID
//        根据id查询遥控帧识别码
        String identifiers = TcFrameConfigIdToIdentified.TC_FRAME_ID_IDENTIFIED.get(id);

//        删除遥控帧配置表中的配置信息
        boolean removeTcFrame = tcFrameConfigService.removeById(id);

//        删除遥控指令配置表中的配置信息
        QueryWrapper<TcCommandConfig> tcCommandQueryWrapper = new QueryWrapper<>();
        tcCommandQueryWrapper.eq("tc_frame_identifiers",identifiers);
        boolean removeTcCommand = tcCommandConfigService.remove(tcCommandQueryWrapper);

//        删除遥控参数配置表中的配置信息
        QueryWrapper<TcParameterConfig> tcParamQueryWrapper = new QueryWrapper<>();
        tcParamQueryWrapper.eq("tc_frame_identifiers",identifiers);
        boolean removeTcParam = tcParameterConfigService.remove(tcParamQueryWrapper);

//        更新全局变量配置信息
        udpSendInitApplication.tcConfigInit();

        if(removeTcFrame & removeTcCommand & removeTcParam){
//            三表都删除成功
            return Result.ok().data("message", "遥控帧删除成功");
        }else{
            return Result.error().data("message", "遥控帧删除失败");
        }
    }

    @ApiOperation(value = "根据ID集合查询遥控指令配置信息")
    @PostMapping("getTcCommandListByIdList")
    public Result getTcCommandListByIdList(
            @ApiParam(name = "idList", value = "待查询的id集合")
            @RequestBody List<String> idList){
        List<TcCommandConfig> tcCommandConfigList = new ArrayList<>();
        for (String id : idList) {
            tcCommandConfigList.add(TcCommandConfigMap.TC_COMMAND_ID_TCCOMMANDCONFIG.get(id));
        }
        return Result.ok().data("tcCommandConfigList",tcCommandConfigList);
    }

    @ApiOperation(value = "指令发送，S/CAN，固定/可变，立即/延时，都在此函数中")
    @PostMapping("tcSend")
    public Result tcSend(
            @ApiParam(name = "tcSendParam", value = "待发送的参数指令类")
            @RequestBody TcSendParam tcSendParam,
            HttpServletRequest request){
//        首先更新遥控表格的配置
//        更新指令表中的延时执行时间
        QueryWrapper<TcCommandConfig> tcCommandConfigQueryWrapper = new QueryWrapper<>();
        tcCommandConfigQueryWrapper.eq("id",tcSendParam.getTcSendCommand().getId());
        TcCommandConfig tcCommandConfig = new TcCommandConfig();
        tcCommandConfig.setTcExecutionTime(tcSendParam.getTcSendCommand().getTcExecutionTime());
        tcCommandConfigService.update(tcCommandConfig,tcCommandConfigQueryWrapper);
//        更新参数表中的用户填写的参数，只更新tcParameterValueD字段
        ArrayList<TcParameterConfig> tcParameterConfigArrayList = tcSendParam.getTcSendForm().getParameters();
//        遍历前端发来的数据
        for (TcParameterConfig tcParameterConfig : tcParameterConfigArrayList) {
            QueryWrapper<TcParameterConfig> tcParameterConfigQueryWrapper = new QueryWrapper<>();
            tcParameterConfigQueryWrapper.eq("id",tcParameterConfig.getId());
            tcParameterConfigService.update(tcParameterConfig,tcParameterConfigQueryWrapper);
        }
//        调用指令组包方法，组成要发出的指令格式，CAN路返回88字节；S路返回128字节
        String tcMessage = sendDataAnalysis.sendDataFraming(tcSendParam);
//        根据发送通路选择发送内容拆包
        if ("s".equalsIgnoreCase(tcSendParam.getTcSendChannel())){
//        前端选择S通路发送
            nettyUdpSendServer.send(tcMessage,sPortConfig.getSendingIP(),sPortConfig.getSendingPort());
            tcInfoLogService.saveTcInfo(request,tcSendParam,tcMessage);
            return Result.ok().data("message","S通路指令发送：" + sPortConfig.getSendingIP() + ":" + sPortConfig.getSendingPort() + "/" + tcMessage);
        }else {
//        前端选择CAN通路发送
            String canId;
            String canData;
            String canFrame;
            for (int i = 0; i < 11; i++) {
                canId = Integer.toHexString(Integer.parseInt(canConfig.getCanId1(),16) + i) + "0000";
                canData = tcMessage.substring(i * 16, i * 16 + 16);
                canFrame = canId + canData;
                nettyUdpSendServer.send(canFrame,canPortConfig.getSendingIP(),canPortConfig.getSendingPort());
            }
            tcInfoLogService.saveTcInfo(request,tcSendParam,tcMessage);
            return Result.ok().data("message","CAN通路指令发送：" + canPortConfig.getSendingIP() + ":" + canPortConfig.getSendingPort() + "/" + tcMessage);
        }
    }

    @ApiOperation(value = "遥控过程指令列表回放")
    @PostMapping("replayTcList")
    public Result replayTcList(
            @ApiParam(name = "replayTime", value = "待查询的时间")
            @RequestBody List<String> replayTime){
        QueryWrapper<TcInfoLog> queryWrapper = new QueryWrapper<>();
        queryWrapper.between("gmt_create",replayTime.get(0),replayTime.get(1));
        queryWrapper.orderByDesc("gmt_create");
        List<TcInfoLog> tcInfoLogList = tcInfoLogService.list(queryWrapper);
        return Result.ok().data("tcInfoLogList",tcInfoLogList);
    }

    @Log(title = "遥控过程指令列表导出", businessType = BusinessType.TC_CONFIG_EXPORT)
    @ApiOperation(value = "遥控过程指令列表导出")
    @PostMapping("exportTcList")
    public void exportTcList(
            @ApiParam(name = "replayTime", value = "待导出的时间范围")
            @RequestBody List<String> replayTime,
            HttpServletResponse response) throws IOException {
//        设置下载信息
        response.setContentType("application/vnd.ms-excel");
        response.setCharacterEncoding("utf-8");
//        这里URLEncoder.encode可以防止中文乱码 当然和easyExcel没有关系
        String fileName = URLEncoder.encode("过程指令下载", StandardCharsets.UTF_8);
        response.setHeader("Content-disposition", "attachment;filename="+ fileName + ".xlsx");
//        封装数据
        List<TcInfoLogExcelVo> tcInfoLogExcelVoList = new ArrayList<>();
//        查询数据
        QueryWrapper<TcInfoLog> queryWrapper = new QueryWrapper<>();
        queryWrapper.between("gmt_create",replayTime.get(0),replayTime.get(1));
        queryWrapper.orderByDesc("gmt_create");
        List<TcInfoLog> tcInfoLogList = tcInfoLogService.list(queryWrapper);
        for (TcInfoLog tcInfoLog : tcInfoLogList) {
            TcInfoLogExcelVo tcInfoLogExcelVo = new TcInfoLogExcelVo();
            BeanUtils.copyProperties(tcInfoLog,tcInfoLogExcelVo);
            tcInfoLogExcelVoList.add(tcInfoLogExcelVo);
        }
//        写入Excel
        EasyExcel.write(response.getOutputStream(), TcInfoLogExcelVo.class).sheet("过程指令记录").doWrite(tcInfoLogExcelVoList);
    }

    @ApiOperation(value = "查询下行设备信息")
    @GetMapping("getTcInstrumentInfo")
    public Result getTcInstrumentInfo(){
        Map<String,String> TcInstrumentInfo = new HashMap<>();
        TcInstrumentInfo.put("canSendPort",canPortConfig.getSendingPort().toString());
        TcInstrumentInfo.put("sSendPort",sPortConfig.getSendingPort().toString());
        TcInstrumentInfo.put("canIP",canPortConfig.getSendingIP());
        TcInstrumentInfo.put("sIP",sPortConfig.getSendingIP());
//        todo
        TcInstrumentInfo.put("xSendPort","not configured yet");
        TcInstrumentInfo.put("xIP","not configured yet");
//        返回数据
        return Result.ok().data("TcInstrumentInfo",TcInstrumentInfo);
    }
}

